package com.aaaa.scheduler.manager.impl;

import com.aaaa.scheduler.manager.IProcessManager;
import com.aaaa.scheduler.objective.Objective;
import com.aaaa.scheduler.objective.ObjectiveCmax;
import com.aaaa.scheduler.pojo.*;
import com.aaaa.scheduler.pojo.Process;
import com.aaaa.scheduler.rule.machinerule.MachineSU;
import lombok.extern.slf4j.Slf4j;

import java.util.List;
import java.util.TreeSet;

@Slf4j
public class ProcessManager implements IProcessManager {
    @Override
    public void assignTask(Instance instance, Process process, Boolean insert, Boolean isGA, CandidateMachine alreadyMachine) throws Exception {
        assignOperation(instance, process, insert, isGA, alreadyMachine);
    }

    /**
     *
     * @description:将工序安排到机床，采用插空方式
     *
     */
    protected void assignOperation(Instance instance, Process process, Boolean insert, Boolean isGA, CandidateMachine alreadyMachine) throws Exception {
        // TODO Auto-generated method stub
        Machine machine = null;
        CandidateMachine candidateMachine = null;
        if(alreadyMachine == null){
            // 寻找这道工序的可选机床
            List<CandidateMachine> candidateMachineList = instance.getCandidateMachineMap().get(process.getName());
            // 刚好有一台可用机床，则直接分派
            if (candidateMachineList.size() == 1) {
                candidateMachine = candidateMachineList.get(0);
            } else {
                // 多于一台机床的情况(如并行机或柔性作业车间调度)
                if (process.getFixedMachineID() != -1) {//这道工序固定了生产设备
                    candidateMachine = candidateMachineList.stream()
                            .filter(can -> can.getMachine().getID() == process.getFixedMachineID()).findAny().get();
                }else{//没有固定的机器，按照一定的规则去选择一台设备
                    MachineSU machineRule = new MachineSU();
                    TreeSet<CandidateMachine> canMachines = new TreeSet<>(machineRule);
                    instance.setCandidateMachineRule(machineRule);
                    canMachines.addAll(candidateMachineList);
                    candidateMachine = canMachines.first();
                    //TODO 设计设备选择比较器。在CandidateMachine比较器里保存一个instance对象，就可以获取Machine进行比较机器的利用率，分派时间等
                }
            }
            machine = candidateMachine.getMachine();
        }else{//遗传算法，解码时的工序分配
            candidateMachine = alreadyMachine;
            machine = candidateMachine.getMachine();
        }
        process.setMachineID(machine.getID());
        process.setMachineName(machine.getName());
        // 如果机床没有安排任何任务，则直接安排
        // 计划开始为最早开始
        process.setStart(process.getPrepOp()==null ? process.getEarlyStart() : process.getPrepOp().getFinish());
        process.setFinish(process.getStart() + candidateMachine.getDuration());
        if (machine.getQueueList().size() != 0){
            findSpace(process, candidateMachine, machine, insert);
        }
        // 将工序添加到机床的队列中
        machine.getQueueList().add(process);
        machine.setAssignedTaskWork(machine.getAssignedTaskWork() + candidateMachine.getDuration());
        instance.setTotalAssignedTaskWork(instance.getTotalAssignedTaskWork() + candidateMachine.getDuration());

        afterAssign(instance, process, candidateMachine, isGA);
        if (machine == null) {
            log.error("无可选机床！");
            throw new Exception(process.getName() + "无可选机床");
        }
    }

    /**
     *
     * @description:插空处理（可选），如果机床上没有安排任务，则工序可以从最早开始加工，如果有任务，则从第一个任务开始依次寻找空隙
     *
     */
    private void findSpace(Process process, CandidateMachine candidateMachine, Machine machine, boolean insert) {
        // TODO Auto-generated method stub
        // 如果有任务了则需要判断插空
        //工序最早可以开始的时间 和最早结束时间
        double earlyStart = process.getPrepOp()==null ? process.getEarlyStart() : process.getPrepOp().getFinish();
        double earlyFinish = earlyStart + candidateMachine.getDuration();
        process.setStart(earlyStart);
        process.setFinish(earlyFinish);
        //设置机器上的前道工序和后道工序
        Process formerOp = new Process();
        formerOp.setStart(0);
        formerOp.setFinish(0);
        Process latterOp = machine.getQueueList().first();
        boolean findSpace = false;
        // 只有需要插空时才寻找空隙
        if (insert) {
            while (!findSpace) {
                findSpace = insertOepration(process, formerOp, latterOp);
                if(!findSpace){
                    formerOp = latterOp;
                    latterOp = machine.getQueueList().higher(latterOp);
                }
            }
        } else {
            // 不插空则直接选择机床上最后一道工序和工序前置工序的完成时间的较大值作为开始
            latterOp = machine.getQueueList().last();
            double startTime = latterOp.getFinish() > process.getEarlyStart() ? latterOp.getFinish()
                    : process.getEarlyStart();
            process.setStart(startTime);
            process.setFinish(startTime + candidateMachine.getDuration());
        }
    }

    /**
     *
     * @author: hba
     * @description:判断机床的空隙是否可以放下当前工序
     * @param formerOp
     *            机床上的前一道工序
     * @param latterOp
     *            机床上的后一道工序
     *
     */
    private boolean insertOepration(Process process, Process formerOp, Process latterOp) {
        // TODO Auto-generated method stub
        double start = 0;
        double finish = 0;
        double workTime = process.getFinish() - process.getStart();
        if (latterOp == null) {
            start = Math.max(process.getStart(), formerOp.getFinish());
//            System.out.println(start);
            finish = start + workTime;
            process.setStart(start);
            process.setFinish(finish);
            return true;
        }
        if(latterOp.getStart() - formerOp.getFinish() < workTime){//空隙不足
            return false;
        }else{//空隙够用
            if(process.getFinish() > latterOp.getStart()){//最早结束时间已经超出后面一道工序的开始时间，工序不能安排在这里
                return false;
            }else{
                start = Math.max(process.getStart(), formerOp.getFinish());
                finish = start + workTime;
                process.setStart(start);
                process.setFinish(finish);
                return true;
            }
        }
    }

    /**
     * 功能描述：每分配一道工序，就更新就序列表：删除已经分派的工序，将下一道工序加入就序列表
     *
     */
    @Override
    public void afterAssign(Instance instance, Process process, CandidateMachine candidateMachine, Boolean isGA) throws Exception {
        //将这个订单的下一道工序加入就序列表
        Process nextOp = process.getSuccOp();
        Product part = process.getProduct();
        part.setCurrOp(nextOp);
        if(!isGA){
            TreeSet<Process> readyTasks = instance.getReadyTaskS();
            boolean remove = readyTasks.remove(process);
            if (nextOp != null) {
                readyTasks.add(nextOp);
            }
        }
        process.setState(Process.Assigned_State);
        //设置已安排工序的准备时间和单间运行时间
        process.setPreTime(candidateMachine.getSetupTime());
        process.setRunTime(candidateMachine.getRunTime());
        if(nextOp == null){
            // 设置工序结束时间为工件结束时间
            part.setFinish(process.getFinish());
        }

        //设置上一次分配的目标值
//        instance.setLastObjectiveValue(instance.getObjective().getObjectiveValue());
        for (int i = 0; i < instance.getLastObjectiveValue().length; i++) {
            instance.getLastObjectiveValue()[i] = instance.getObjectiveList().get(i).getObjectiveValue();
        }

        //计算本次分配的目标值
//        double cmax = instance.getObjective().calcValue(instance);
        double cmax = 0;
        for (Objective objective : instance.getObjectiveList()) {
            objective.calcValue(instance);
            if(objective.getClass() == ObjectiveCmax.class){
                cmax = objective.getObjectiveValue();
            }
        }
//        System.out.println("cmax:" + cmax);
        //本次分配后的 当前利用率计算
        for (Machine machine : instance.getMachineMap().values()) {
//            double util = 0;
//            double load = 0;
//            for (Process p : machine.getQueueList()) {
//                load += p.getWorkTime();
//                util += p.getRunTime() * p.getPlanQty();
//            }
//            double loadRation = load / cmax;
//            double utilRation = util / cmax;
//            machine.setUtilRation(utilRation);
//            machine.setLoadRation(loadRation);
            //设备已分配的总工时/最大制造期
            machine.setUtilRation(machine.getAssignedTaskWork()/cmax);
            machine.setLoadRation(machine.getAssignedTaskWork());
        }
    }

    @Override
    public void removeFromReadyTasks(Process process) {

    }


}
